package com.cory.utils.file;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.cory.utils.lang.ExceptionUtils;


/**
 * 视频处理工具类
 * 
 * @author xc
 * @version 2017-01-22
 */
public class VideoUtils {
	
	        /**
     * ffmpeg执行文件路径
     */
    public static final String FFMPEG_PATH = "D:\\video\\ffmpeg";
	
	        /**
     * ffmpeg能解析的格式：（asf，mpg，wmv，3gp，mp4，mov，avi，flv等）
     */
	private static final String[] FFMPEG_FORMATS = {"asf", "mpg", "wmv", "3gp", "mp4", "mov", "avi", "flv"};
	
	        /**
     * mencoder执行文件路径
     */
	private static final String MENCODER_PATH = "D:\\video\\mencoder";
	
	        /**
     * mencoder对ffmpeg补充的格式
     */
	private static final String[] MENCODER_FORMATS = {"wmv9", "rm", "rmvb"};
	
	        /**
     * 视频格式转换
     */
	public static void convert(String src, String dest) throws IOException {
		convert(src, dest, false);
	}
	
	        /**
     * 视频格式转换 isSync是否同步
     */
	public static void convert(String src, String dest, Boolean isSync) throws IOException {
		if (!new File(src).exists()) {
			throw new IOException("src video is not exists");
		}
		
		String srcFormat = src.substring(src.lastIndexOf(".") + 1, src.length()).toLowerCase();
		String destFormat = dest.substring(dest.lastIndexOf(".") + 1, dest.length()).toLowerCase();
		
		if(ffmpegFormat(srcFormat) && ffmpegFormat(destFormat)) {
			ffmpegConvert(src, dest, isSync);
			return;
		}
		
		if(mencoderFormat(srcFormat) && (ffmpegFormat(destFormat) || mencoderFormat(destFormat))) {
			mencoderConvert(src, dest, isSync);
			return;
		}
		
		throw new IOException(srcFormat + " convert to " + destFormat + " is not support");
	}
	
	        /**
     * 视频格式转换（ffmpeg）
     */
	private static void ffmpegConvert(String src, String dest, Boolean isSync) throws IOException {
		List<String> commend = new java.util.ArrayList<String>();
		
		commend.add(FFMPEG_PATH);
		commend.add("-i");  
        commend.add(src);
        
        if (dest.endsWith(".3gp")) {
	        commend.add("-s");  
	        commend.add("sqcif");
	        commend.add("-acodec"); 
	        commend.add("amr_wb");
	        commend.add("-ar");  
	        commend.add("16000"); 
        } else {
        	commend.add("-acodec");  
        	commend.add("libmp3lame");
	    	commend.add("-ar");  
	        commend.add("22050");
        }
        commend.add("-ab");  
        commend.add("128");
        commend.add("-ac");  
        commend.add("1"); 
        commend.add("-r");  
        commend.add("29.97");

        commend.add("-y");
        commend.add(dest); 
        
        process(commend, isSync); 
	}
	
	        /**
     * 视频格式转换（mencoder）
     */
	private static void mencoderConvert(String src, String dest, Boolean isSync) throws IOException {
		List<String> commend = new java.util.ArrayList<String>();
		
		commend.add(MENCODER_PATH);
        commend.add(src);
        
        commend.add("-oac");  
        commend.add("mp3lame");  
        commend.add("-lameopts");  
        commend.add("preset=64");  
        commend.add("-ovc");  
        commend.add("xvid");  
        commend.add("-xvidencopts");  
        commend.add("bitrate=600");  
        
        commend.add("-o"); 
        commend.add(dest); 
        
        process(commend, isSync);
	}
	
	        /**
     * 视频剪切
     */
	public static void cut(String src, String dest, String startTime, String cutTime) throws IOException {
		cut(src, dest, startTime, cutTime, false);
	}
	
	        /**
     * 视频剪切 startTime 开始时间（hh:mm:ss） cutTime 持续时间（hh:mm:ss）
     */
	public static void cut(String src, String dest, String startTime, String cutTime, Boolean isSync) throws IOException {
		if (!new File(src).exists()) {
			throw new IOException("src video is not exists");
		}
		
		String srcFormat = src.substring(src.lastIndexOf(".") + 1, src.length()).toLowerCase();
		String destFormat = dest.substring(dest.lastIndexOf(".") + 1, dest.length()).toLowerCase();
		if (!ffmpegFormat(srcFormat) || !ffmpegFormat(destFormat)) {
			throw new IOException(srcFormat + " or " + destFormat + " is not support");
		}
		
		List<String> commend = new java.util.ArrayList<String>();
		
		commend.add(FFMPEG_PATH);
		commend.add("-ss");
		commend.add(startTime);
		commend.add("-t");
		commend.add(cutTime);   
		
		commend.add("-i");
		commend.add(src);
		if (dest.endsWith(".3gp")) {
	        commend.add("-s");  
	        commend.add("sqcif");
	        commend.add("-acodec"); 
	        commend.add("amr_wb");
	        commend.add("-ar");  
	        commend.add("16000"); 
	        commend.add("-ac");  
	        commend.add("1"); 
        }
		commend.add("-y");
		commend.add(dest);
		
		process(commend, isSync);
	}
	
	        /**
     * 视频总时间 格式:hh:mm:ss.SSS
     */
	public static String getTime(String src) throws IOException {
		if (!new File(src).exists()) {
			throw new IOException("src video is not exists");
		}
		
		String srcFormat = src.substring(src.lastIndexOf(".") + 1, src.length()).toLowerCase();
		if (!ffmpegFormat(srcFormat)) {
			throw new IOException(srcFormat + " is not support");
		}
		
		List<String> commend = new java.util.ArrayList<String>();
		commend.add(FFMPEG_PATH);  
		commend.add("-i");  
		commend.add(src);
		
		String result = process(commend, true);
		
		String regex = "Duration: (.*?), start: (.*?), bitrate: (\\d*) kb\\/s";  
        Pattern pattern = Pattern.compile(regex); 
        Matcher m = pattern.matcher(result.toString());
        if (m.find()) {
        	return m.group(1);
        }
		return null;
	}
	
	        /**
     * 判断ffmpeg能否解析格式
     */
	private static Boolean ffmpegFormat(String format) {
		for (String str : FFMPEG_FORMATS) {
			if (str.equals(format))
				return true;
		}
		return false;
	}
	
	        /**
     * 判断是否mencoder补充的格式
     */
	private static Boolean mencoderFormat(String format) {
		for (String str : MENCODER_FORMATS) {
			if (str.equals(format))
				return true;
		}
		return false;
	}
	
	        /**
     * 执行命令 isSync是否同步
     */
	private static String process(List<String> commend, Boolean isSync) throws IOException {
		ProcessBuilder builder = new ProcessBuilder();
        builder.command(commend);  
        builder.redirectErrorStream(true);
        Process p = builder.start();

        BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
        String line = null;
        StringBuffer sb = new StringBuffer();
        while((line = br.readLine()) != null){
        	sb.append(line);
        }
        br.close();
        
        if (isSync) {
        	try {
        		p.waitFor();
        	} catch(InterruptedException e) {
        		throw ExceptionUtils.unchecked(e);
        	}
        }
        
        return sb.toString();
	}
}
